# -*- coding:utf-8 -*-
"""
作者：MySoul
日期：2023年10月13日
"""
import json
import math
import random
import time
from xmlrpc.client import ResponseError

import requests
from minio import Minio
from selenium import webdriver
from selenium.common import NoSuchElementException
from selenium.webdriver import Chrome
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait

from pojo.Attachment import Attachment
from pojo.NoticeReceived import NoticeReceived
from pojo.Status import state
from service import ConfigService, LogRecoderService
from utils import Util


class SchoolEducation:
    # 类对象生命周期开始时调用
    def __init__(self, config_service, options, service):
        # 1.基于Chrome的浏览器初始化
        self.web = Chrome(options=options, service=service)

        # 2.全局超时时间初始化
        self.timeWait = ConfigService.get_config(config_service, 'script', 'time_wait')
        self.web.set_page_load_timeout(self.timeWait)  # 设置全局页面超时时间

        # 3.获取账号信息
        self.account = ConfigService.get_config(config_service, 'init', 'account')
        self.config_service = config_service
        self.data = []  # 设置数据保存空间
        self.latestTime = ""  # 记录最新时间
        self.retryLoginCount = 0  # 登录超时重试次数
        self.retryNoticeReceiveCount = 0  # 获取消息重试次数
        self.retryPNCount = 0  # 获取公告重试次数
        self.retryPushCount = 0  # 获取公告重试次数
        self.minioClient = Minio(  # 初始化MinIO信息
            endpoint=ConfigService.get_config(config_service, 'minio', 'endpoint'),  # MinIO 服务器的地址和端口
            access_key=ConfigService.get_config(config_service, 'minio', 'access_key'),  # MinIO 的访问密钥
            secret_key=ConfigService.get_config(config_service, 'minio', 'secret_key'),  # MinIO 的私密密钥
            secure=False  # 禁用安全连接（HTTPS）
        )

    # 设置此方法程序最大重试次数，单位/次、等待时间间隔，单位/ms

    # @retry(stop_max_attempt_number=3, wait_fixed=3000)
    def checkAndLogin(self):
        """
        检查并登录账号
        """
        # 1.登录预处理
        # 1.1统计此方法重试次数
        self.retryLoginCount += 1
        LogRecoderService.logger.info(f"开始登录,第{self.retryLoginCount}次")

        # 2.判断初始账号、账号过期，实现账号登录
        loginStatus = False
        result, cookies = Util.read_cookie_from_file(
            f"{ConfigService.get_config(self.config_service, 'script', 'cookie_address') + self.account}.txt")
        if result == loginStatus:
            # 2.1是初始账号，准备登录
            LogRecoderService.logger.info("此账号为初始账号，需要登录认证！")
        else:
            loginStatus = result  # 不为初始账号

        # 2.2不为初始账号，判断账号Cookie是否过期
        if loginStatus:
            LogRecoderService.logger.info("正在检测cookie是否过期……")

            # 2.2.1前往登录认证
            self.web.get("https://jiaowu.scetc.edu.cn/")

            # 2.2.2添加Cookie
            for cookie in cookies:
                cookie_dict = {
                    'domain': '.scetc.edu.cn',  # 为什么此处填入子域名，而不是全域名
                    'name': cookie.get('name'),
                    'value': cookie.get('value'),
                    "expires": '',
                    'path': '/',
                    'httpOnly': False,
                    'sameSite': "Lax",
                    'secure': False,
                }
                self.web.add_cookie(cookie_dict)

            # 2.2.3验证Cookie

            self.web.get("https://jiaowu.scetc.edu.cn/jsxsd/ggly/ysgg_query?pageIndex=1")

            # 2.2.3.1获取网页标题
            title = self.web.title
            text = title.strip()

            # 2.2.3.2判断页面状态
            if "登录" in text:
                # 2.2.3.2.1打印日志
                LogRecoderService.logger.info("账号过期，需要进行登录认证！")
                # 2.2.3.2.2删除Cookie
                Util.remove_cookie_from_file(
                    f"{ConfigService.get_config(self.config_service, 'script', 'cookie_address') + self.account}.txt")
                # 2.2.3.2.清理Cookie
                self.web.delete_all_cookies()
            else:
                LogRecoderService.logger.info("账号已保持登录，无需进行登录认证！")
                return state.success

        # 2.3此账号为初始账号或登录过期，需要重新登录
        # 2.3.1读取密码
        password = ConfigService.get_config(self.config_service, 'init', 'password')
        # 2.3.2登录请求
        self.web.get("https://sso.scetc.edu.cn/login?service=https:%2F%2Fjiaowu.scetc.edu.cn%2Fsso.jsp")  #
        # 此处容易发生TimeoutException
        self.web.refresh()  # 强制刷新，确保页面元素加载完整

        LogRecoderService.logger.info("登录中……")

        # 判断是否进入选课界面
        text = self.web.title
        if text in "入口选择":
            LogRecoderService.logger.info("登录成功~")
            return state.success
        else:
            pass

            # 2.3.2.1获取待抓取的表单节点
        result = WebDriverWait(self.web, self.timeWait, 0.5).until(  # 显性等待，在10秒内每0.5秒调用until中方法,当等待成功返回True否则超时为False
            EC.presence_of_all_elements_located((By.XPATH, "//nz-input-group/input"))  # 此处容易发生TimeoutException
        )

        # 2.3.2.2输入账号和密码，在此之前，清除输入内容，防止此方法重试时添加数据
        try:
            time.sleep(0.5)
            result[0].clear()
            result[0].send_keys(self.account)  # 输入账号
            time.sleep(0.5)
            result[1].clear()
            result[1].send_keys(password)  # 输入密码
        except IndexError:
            tips = f"网页元素超出索引错误，请检查网页结构是否更新！！！{self.web.current_url}"
            LogRecoderService.logger.error(tips)
            # 将错误记录文件，准备推送
            Util.write_error_file(ConfigService.get_config(self.config_service, 'script', 'error_path'), tips)
        # 2.3.2.3获取登录按钮结点
        result = WebDriverWait(self.web, self.timeWait, 0.5).until(
            EC.presence_of_element_located((By.TAG_NAME, "button"))  # 此处容易发生TimeoutException
        )

        # 2.3.2.4点击登录
        result.click()  # 此处容易发生TimeoutException
        # 2.2.3.2判断页面状态
        loginCount = 0
        while True:
            text = self.web.title
            if "登录" in text:
                LogRecoderService.logger.info("账号登录不成功，即将重新登录！")
                raise TimeoutError
            else:
                loginCount += 1
                time.sleep(0.25)
            if loginCount > 3:
                LogRecoderService.logger.info("登录成功~")
                break

        # 2.3.3获取并保存当前会话中的所有cookie
        cookies = self.web.get_cookies()

        Util.save_cookie_to_file(
            f"{ConfigService.get_config(self.config_service, 'script', 'cookie_address') + self.account}.txt", cookies)
        return state.success

    # @retry(stop_max_attempt_number=20, wait_fixed=3000)
    def getNoticeReceived(self):
        """
        获取已收公告的信息条目逻辑
        :return:
        """
        # 1.获取通知预处理
        # 1.1统计此方法重试次数
        self.retryNoticeReceiveCount += 1
        LogRecoderService.logger.info(f"正在获取最新消息,第{self.retryNoticeReceiveCount}次")

        # 2.获取通知信息
        # 2.1页面url检测，防止页面多次刷新导致账号异常登出
        if "jsxsd/ggly/ysgg_query" not in self.web.current_url:
            self.web.get("https://jiaowu.scetc.edu.cn/jsxsd/ggly/ysgg_query?pageIndex=1")
        else:
            pass

        text = self.web.title
        if "登录" in text:
            LogRecoderService.logger.info("账号登录不成功，即将重新登录！")
            return state.error
        LogRecoderService.logger.info("获取基本信息中……")

        # 2.2获取页面基本信息，包括：网页总条数、分页条数，便于计算公告条目个数
        result = WebDriverWait(self.web, self.timeWait, 0.5).until(  # 此处容易发生TimeoutException
            EC.presence_of_element_located((By.XPATH, '//*[@id="Form1"]/div'))
        )
        try:
            totalNumber = result.find_element(By.XPATH, "./span[2]").text.strip()
            pageNumber = result.find_element(By.XPATH, "./p").text.strip()
            totalNumber = Util.getNumberFromStr(totalNumber)[0]
            pageNumber = Util.getNumberFromStr(pageNumber)[0]
        except NoSuchElementException:
            tips = f"网页元素丢失错误，请检查网页结构是否更新！！！{self.web.current_url}"
            Util.write_error_file(ConfigService.get_config(self.config_service, 'script', 'error_path'), tips)
            return state.error
        except IndexError:
            tips = f"网页元素超出索引错误，请检查网页结构是否更新！！！{self.web.current_url}"
            Util.write_error_file(ConfigService.get_config(self.config_service, 'script', 'error_path'), tips)
            return state.error

        # 2.3读取最新时间
        pathFile = f"{ConfigService.get_config(self.config_service, 'script', 'latest_time')}ysgg_query_{self.account}.txt"
        self.latestTime = Util.getLatestTime(pathFile)

        # 2.4获取公告条目
        # 2.4.1计算待获取的页数
        rangeCount = math.ceil(int(totalNumber) / int(pageNumber)) + 1
        for page in range(1, rangeCount):

            # 2.4.2获取信息位置打印
            LogRecoderService.logger.info(f"获取信息中:page/{page}")

            # 2.4.3获取信息内容
            flag, status = self.__getPublicItem()
            if status != state.success:
                # 获取错误，则返回错误原因
                return state.error
            if flag:
                # 获取成功，则结束
                break
            # 2.4.4切换页面
            self.web.get(f"https://jiaowu.scetc.edu.cn/jsxsd/ggly/ysgg_query?pageIndex={page + 1}")

        # 2.5获取公告信息打印
        LogRecoderService.logger.info("获取完毕~")
        LogRecoderService.logger.info(f"data:{self.data}")
        LogRecoderService.logger.info(f"共获取到{len(self.data)}条数据！")
        return state.success

    def getDetailMessage(self):
        """
        获取已收公告的信息条目详细内容逻辑
        :return:
        """
        # 1.获取已收公告长度
        data_range = len(self.data)

        tips = ""
        # 2.获取已收公告详细内容
        if data_range:
            # 2.1获取消息预处理
            # 2.1.1统计此方法重试次数
            self.retryPNCount += 1
            LogRecoderService.logger.info(f"正在获取详细信息,第{self.retryPNCount}次")

            # 2.2倒序循环，从未获取的最后一条消息开始获取，并实现推送
            for page in range(data_range, 0, -1):
                # 2.2.1窗口最大化，保证截图的完整
                self.web.maximize_window()
                # 2.2.2设置随机睡眠时间，绕开教务检测异常，防止账号异常登出
                time.sleep(random.uniform(1, 2))
                tempPage = page - 1  # 数组下标减1
                # 2.2.3获取公告的详细内容截屏
                url = self.data[tempPage]["imgSrc"]
                LogRecoderService.logger.info(f'正在获取本条详细信息Url:{url}')
                status, tips = self.__getDetailInfo(url, tempPage)
                if status != state.success:
                    return state.error
                # 2.2.4检测并获取公告的内容附件
                status, tips = self.__getDetailAtt(tempPage)
                if status != state.success:
                    return state.error
                # 2.2.5推送获取公告的内容消息
                status, tips = self.__pushItemToServer(self.data[tempPage])
                if status != state.success:
                    return state.error
            tips = "获取成功~"
            LogRecoderService.logger.info(tips)
        else:
            tips = "数据为空，无需获取！！"
            LogRecoderService.logger.info(tips)
        LogRecoderService.logger.info(f"data:{self.data}")
        LogRecoderService.logger.info(f"共保存{len(self.data)}条信息！")
        return state.success

    # @retry(stop_max_attempt_number=2, wait_fixed=3000)
    def __getDetailInfo(self, url, page):
        """
        获取已收公告的信息条目详细内容的截屏信息
        :param url: 跳转链接的Url
        :param page: 设置当前已收公告数据的
        :return: 消息提示
        """
        # 1.跳转详细页面准备获取
        self.web.get(url)  # 此处容易出现超时
        # 1.1调整网页的缩放为100%
        self.web.execute_script('document.body.style.zoom = "100%";')
        # 1.2设置隐性等待时长，让所截图界面加载完整
        self.web.implicitly_wait(self.timeWait)
        # 1.3设置页面宽高
        # 1.3.1获取详细信息宽高
        width = self.web.execute_script("return document.documentElement.scrollWidth")
        height = self.web.execute_script("return document.documentElement.scrollHeight")
        # 1.3.2修改浏览器窗口大小
        self.web.set_window_size(width + 100, height + 100)
        # 1.3.3让脚本沉睡1秒，使获取详细内容之间产生间隔
        time.sleep(1)
        # 2.将屏幕截图保存为文件
        # 2.1设置保存到本地的图片路径
        fileName = "NoticeReceived.png"
        filePath = f"{ConfigService.get_config(self.config_service, 'script', 'screen_short')}{fileName}"
        # 2.2保存图片
        self.web.save_screenshot(filePath)
        # 2.3设置推送到服务端的文件名
        fileName = self.data[page]["releaseDate"] + ".png"
        # 3.获取图片公网路径
        # 3.1获取图片公网链接
        LogRecoderService.logger.info("正在上传详细信息……")
        status, src = self.__getPublicUrl(type="image", fileName=fileName, filePath=filePath)
        if status == state.success:
            LogRecoderService.logger.info("详细信息，保存成功~")
        else:
            LogRecoderService.logger.error(src)
            return state.error, src
        # 3.2重新回填数据
        self.data[page]["imgSrc"] = src
        return state.success, "获取成功~"

    # @retry(stop_max_attempt_number=1, wait_fixed=3000)
    def __getDetailAtt(self, page):
        """
        获取已收公告的信息条目详细内容的附件信息
        :param page:设置当前已收公告数据的索引
        :return: 消息提示
        """
        # 1.获取附件总数及其链接
        # 1.1获取页面中附件链接
        result = WebDriverWait(self.web, self.timeWait, 0.5).until(  # 显性等待，在10秒内每0.5秒调用until中方法,当等待成功返回True否则超时为False
            EC.presence_of_all_elements_located((By.XPATH, "/html/body/table"))  # 此处容易发生TimeoutException
        )
        alist = result[0].find_elements(By.TAG_NAME, "a")

        # 1.2获取页面中附件元素个数
        tempRange = len(alist)
        attachments = []
        attachMentCount = 0
        for a in range(tempRange):
            # 2.获取疑似链接信息
            # 2.1获取链接标题
            finalFileName = alist[a].text.strip()
            # 2.2判断该链接是否为所需获取的附件
            hasFlag = Util.hasSupportedExtension(finalFileName)
            # 2.3若是附件，则获取。否则，跳过-----此处下载不建议使用click的方式进行点击下载，因为浏览器会默认弹窗进行路径保存
            if hasFlag:
                LogRecoderService.logger.info(f"正在获取该附件:{finalFileName}")
                # 2.3.1区分链接，此教务中有两种，分别是onclick、href
                onclickAttr = alist[a].get_attribute("onclick")  # 判断是tr[2]中的属性
                resultUrl = ""
                # 2.3.2区分onclick和href链接
                if onclickAttr is None:
                    # 2.3.2.1是href链接
                    hrefAttr = alist[a].get_attribute("href")
                    resultUrl = "http://jiaowu.scetc.edu.cn" + hrefAttr
                else:
                    # 2.3.2.2是onclick链接
                    resultUrl = "http://jiaowu.scetc.edu.cn/jsxsd/ggly/gglyFileDown.do?ggid=" + Util.getUrlFromStrTwo(
                        onclickAttr)

                # 2.3.2判断所下载的附件链接是否已经失效
                code = Util.getNetWorkStatus(resultUrl)
                if "200" != code:
                    if "403" == code:
                        tips = "Invalid value"
                        LogRecoderService.logger.info(tips)
                        Util.write_error_file(ConfigService.get_config(self.config_service, 'script', 'error_path'),
                                              tips)
                    continue  # 结束当前附件获取，准备获取下一个附件

                # 2.3.3判断所下载的附件链接是否是在线预览链接
                flagPreview = Util.checkContentType(resultUrl)
                src = ""
                # 2.3.3.1是在线预览链接
                if flagPreview:
                    src = resultUrl
                else:
                    # 2.3.3.2不是在线预览链接
                    # 2.3.3.2.1下载附件
                    self.web.get(resultUrl)
                    downloadTimeCount = 0
                    while True:
                        # 获取该路径下的所有附件
                        filePath = Util.getAllFiles(
                            ConfigService.get_config(self.config_service, 'script', 'file_download'))
                        if len(filePath) > 0 and filePath[0].split(".")[-1] != "tmp" and filePath[0].split(".")[
                            -1] != "crdownload":
                            break
                        else:
                            downloadTimeCount += 1
                            time.sleep(1)  # 设置随机睡眠时间，保持附件下载完整
                        if downloadTimeCount > 30:  # 强制结束循环，防止程序卡死
                            break
                    attachMentCount += 1  # 附件下载成功后附件个数+1
                    filePath = filePath[0]
                    # 2.3.3.2.2获取附件名后缀
                    fileName = self.data[page]["releaseDate"] + str(attachMentCount) + "." + Util.getSuffix(
                        finalFileName)
                    LogRecoderService.logger.info("正在上传附件……")
                    status, src = self.__getPublicUrl(type="file", fileName=fileName, filePath=filePath)
                    if status == state.success:
                        LogRecoderService.logger.info("详细附件，保存成功~")
                    else:
                        LogRecoderService.logger.error(src)
                        return state.error, src
                attachment = Attachment(name=finalFileName, src=src).__dict__
                attachments.append(attachment)
            else:
                LogRecoderService.logger.warning(f"该链接不是待获取附件:{finalFileName}")
        self.data[page]["fileSrc"] = attachments
        return state.success, "获取成功~"

    # @retry(stop_max_attempt_number=10, wait_fixed=3000)
    def __getPublicItem(self):
        """
        获取已收公告的信息条目的实现方法
        :return:
        """
        # 1.判定公告获取完毕记录
        flag = False

        # 2.获取本页面所有数据
        # 2.1等待页面元素展示完毕再获取
        result = WebDriverWait(self.web, self.timeWait, 0.5).until(  # 此处容易发生TimeoutException
            EC.presence_of_all_elements_located((By.TAG_NAME, 'tr'))
        )

        # 2.2获取本页面最新所有数据
        try:
            for item in result[1:]:  # 此处倒序获取，从下至上便于消息顺序推送

                # 2.2.1获取信息时间
                releaseDate = item.find_element(By.XPATH, "./td[4]").text.strip()
                releaseDate = releaseDate.strip()

                # 2.2.2判断最新时间
                flag = Util.timeCompare(self.latestTime, releaseDate)

                # 2.2.2.1结果为True则表示通知获取完毕，停止获取
                if flag:
                    break

                # 2.2.2.2结果为False则表示通知还有未获取完，继续获取
                # 2.2.3获取公告消息
                title = item.find_element(By.XPATH, "./td[2]").text.strip()  # 标题
                category = item.find_element(By.XPATH, "./td[3]").text.strip()  # 类别
                href = item.find_element(By.XPATH, "./td[5]/a").get_attribute("href")  # 访问链接，补充：获取属性使用get_attribute
                url = "https://jiaowu.scetc.edu.cn" + Util.getUrlFromStr(href)

                # 2.2.4记录公告信息
                self.data.append(
                    NoticeReceived(name="已收公告", title=title, category=category, releaseDate=releaseDate,
                                   imgSrc=url, fileSrc=None).__dict__)

        except IndexError:
            tips = f"网页元素超出索引错误，请检查网页结构是否更新！！！{self.web.current_url}"
            LogRecoderService.logger.info(tips)
            Util.write_error_file(ConfigService.get_config(self.config_service, 'script', 'error_path'), tips)
            return flag, state.error
        except NoSuchElementException:
            tips = f"网页元素丢失错误，请检查网页结构是否更新！！！{self.web.current_url}"
            LogRecoderService.logger.info(tips)
            Util.write_error_file(ConfigService.get_config(self.config_service, 'script', 'error_path'), tips)
            return flag, state.error
        return flag, state.success

    def __getPublicUrl(self, type, fileName, filePath):
        """
        推送达到存储详细附件或详细通知信息的方法
        :param type:   图片或者文件
        :param fileName: 文件名
        :param filePath: 文件存储路径
        :return:
        """
        # 1.检查存储桶
        buckName = "educational"
        try:
            # 1.1.所需存储桶存在则通过
            if self.minioClient.bucket_exists(buckName):
                pass
            else:
                # 1.2.所需存储桶不存在则创建
                self.minioClient.make_bucket(buckName, location='cn-north-1')
        except ResponseError as err:
            Util.write_error_file(ConfigService.get_config(self.config_service, 'script', 'error_path'), err)
            return state.error, ""

        # 2.推送相应文件到MinIo
        try:
            # 2.1推送相应文件，并获取公网访问路径
            self.minioClient.fput_object(buckName, f"{type}/{fileName}", filePath)
        except ResponseError as err:
            tips = f"MinIo对象存储错误，请检查MinIo对象服务:{err}"
            LogRecoderService.logger.info(tips)
            Util.write_error_file(ConfigService.get_config(self.config_service, 'script', 'error_path'), tips)
            return state.error, ""
        except Exception as e:
            tips = f"MinIo对象存储错误，请检查MinIo对象服务:{e}"
            LogRecoderService.logger.info(tips)
            Util.write_error_file(ConfigService.get_config(self.config_service, 'script', 'error_path'), tips)
            return state.error, ""
        finally:
            # 2.2推送完毕后清理本地文件夹保存的路径
            Util.removeAllFiles(ConfigService.get_config(self.config_service, 'script', 'file_download'))
            # 2.3拼接公网访问文件的路径信息
            savePath = ConfigService.get_config(self.config_service, 'minio', 'save_path')
        return state.success, f"{savePath}/{type}/{fileName}"

    # @retry(stop_max_attempt_number=10, wait_fixed=3000)
    def __pushItemToServer(self, noticeReceived):
        # 1.判断是否存在通知消息
        if noticeReceived is not None:
            # 2.消息推送预处理
            self.retryPushCount += 1
            LogRecoderService.logger.info(f"正在推送消息,第{self.retryPushCount}次")
            root_url = ConfigService.get_config(self.config_service, "server", "root_url")  # 获取服务器根路径
            message = ConfigService.get_config(self.config_service, "server", "message")  # 获取message路径
            notice = ConfigService.get_config(self.config_service, 'server', 'notice')  # 获取通知路径
            service_url = root_url + message + notice
            jsonStr = json.dumps(noticeReceived)
            # 3.推送消息到Java服务中
            response = requests.post(service_url, data=jsonStr)
            #  4.Java服务响应成功
            if "200" == str(response.status_code):
                response = json.loads(response.text)
                responseCode = response["code"]
                tips = response["msg"]
                # 4.1判断返回状态码
                if "200" == responseCode or "201" == responseCode:
                    LogRecoderService.logger.info(tips)
                    pathFile = f"{ConfigService.get_config(self.config_service, 'script', 'latest_time')}ysgg_query_{self.account}.txt"
                    # 4.2.数据存取并推送成功，更新最新爬取的时间
                    Util.writeLatestTime(pathFile, noticeReceived["releaseDate"])
                elif "500" == responseCode:
                    LogRecoderService.logger.info(tips)
                    return state.error, tips
                # 5.Java服务响应失败
            else:
                tips = "当前Java服务已宕机，请联系管理员查看!!!"
                LogRecoderService.logger.info(tips)
                Util.write_error_file(ConfigService.get_config(self.config_service, 'script', 'error_path'), tips)
                return state.error, tips
        return state.success, "通知信息已保存完毕~"

    def close(self):
        """
             类对象生命周期结束时调用
        """
        # 2.3.3获取并保存当前会话中的所有cookie
        cookies = self.web.get_cookies()
        Util.save_cookie_to_file(
            f"{ConfigService.get_config(self.config_service, 'script', 'cookie_address') + self.account}.txt", cookies)
        if self.web:
            self.web.close()
            self.web = None
        if self.minioClient:
            self.minioClient = None
            del self.minioClient


# 教务管理处理之前，主要根据服务配置以及配置文件配置获得教务管理对象
def initEducational(config_service):
    # 1.读取配置文件中的基本信息
    # 1.1获取文件下载保存路径
    file_download = ConfigService.get_config(config_service, "script", "file_download")
    # 1.2获取浏览器驱动路径
    browser_driver = ConfigService.get_config(config_service, "script", "browser_driver")
    # 2.创建浏览器选项对象以便配置浏览器启动方式
    options = webdriver.ChromeOptions()
    # 设置用户代理
    options.add_argument(
        'user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"')
    # 禁用沙盒模式
    options.add_argument("--no-sandbox")
    # 禁用GPU加速
    options.add_argument("--disable-gpu")
    # 禁用/dev/shm使用
    options.add_argument("--disable-dev-shm-usage")
    # 启用无头浏览器模式
    options.add_argument("--headless=new")
    # 添加实验性选项
    prefs = {
        # 防止保存弹窗
        'profile.default_content_settings.popups': 0,
        # 允许多文件下载
        "profile.default_content_setting_values.automatic_downloads": 1,
        # 设置浏览器下载路径
        "download.default_directory": file_download,
    }
    options.add_experimental_option("prefs", prefs)

    # 3.创建驱动服务对象
    service = Service(browser_driver)  # 创建驱动服务对象
    return options, service


def main(config_service):
    # 1.记录错误总次数
    err_count = 2
    while err_count:
        # 2.初始化消息信息对象
        # 2.1获取浏览器基本信息
        options, service = initEducational(config_service)
        # 2.2初始化信息对象
        educational = SchoolEducation(config_service, options, service)

        # 3.获取信息
        # 3.1重启次数最大循环两次
        err_count -= 1
        # 3.2检查并登录
        loginStatus = educational.checkAndLogin()
        if loginStatus != state.success:
            continue
        # 3.3获取通知消息
        noticeStatus = educational.getNoticeReceived()
        if noticeStatus != state.success:
            continue
        # 3.4获取详细消息
        detailStatus = educational.getDetailMessage()
        if detailStatus != state.success:
            continue
        # 3.4结束消息获取
        educational.close()
        break
